/*
 * main.cpp
 *
 *  Created on: 2010/04/24
 *      Author: mitsu
 */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <NyuTree.hpp>
#include <NyuContext.hpp>
#include <NyuObject.hpp>
#include <NyuString.hpp>
#include <NyuStrPool.hpp>

extern FILE* yyin;
extern int yyparse(void* parse_result);

NyuValue nyu_print(NyuContext* c)
{
    for (int i = 1, j = c->current_argc(); i < j; i++) {
        NyuValue v = c->current_argv(i);

        switch (NYU_VALUE_TAG(v)) {
        case NYU_VALUE_TAG_INT:
            std::cout << NYU_VALUE_TO_INT(v);
            break;

        case NYU_VALUE_TAG_NATIVE:
            std::cout << "NATIVE(" << NYU_VALUE_UNTAG_NATIVE(v) << ")";
            break;

        case NYU_VALUE_TAG_OBJECT:
            {
                NyuObject* obj = NYU_VALUE_UNTAG_OBJECT(v);
                NyuString* str = obj->to_str(c);
                std::cout << str->strval();
            }
            break;

        case NYU_VALUE_TAG_BLOCK:
            std::cout << "BLOCK(" << NYU_VALUE_UNTAG_BLOCK(v) << ")";
            break;
        }
    }

    std::cout << std::flush;

    return 0;
}

NyuValue nyu_println(NyuContext* c)
{
    nyu_print(c);
    std::cout << std::endl;
    return 0;
}

NyuValue nyu_load(NyuContext* c)
{
    NyuValue ret = 0;

    for (int i = 1, j = c->current_argc(); i < j; i++) {
        NyuValue v = c->current_argv(i);
        if (!NYU_VALUE_IS_OBJECT(v))
            continue;

        NyuString* s = NYU_VALUE_UNTAG_OBJECT(v)->to_str(c);

        // TODO lexerをなんとかしてくれ
        yyin = ::fopen(s->strval(), "r");
        if (!yyin) {
            ::perror(s->strval());
            continue;
        }

        NyuTree* tree;
        if (yyparse(&tree) != 0) {
            std::cerr << "ParseError" << std::endl;
            ::fclose(yyin);
            continue;
        }

        ::fclose(yyin);

        ret = tree->eval(c);
    }

    return ret;
}

int main(int argc, char** argv)
{
    // TODO ファイル指定がなければ対話モードに入る
    if (argc < 2) {
        std::cerr << "usage: " << argv[0] << " file.nyu" << std::endl;
        return 1;
    }

    // TODO lexerをなんとかする
    yyin = ::fopen(argv[1], "r");
    if (!yyin) {
        ::perror(argv[1]);
        return 1;
    }

	NyuContext* c = new NyuContext();

    c->set_func("print", nyu_print);
    c->set_func("println", nyu_println);
    c->set_func("load", nyu_load);

    // コマンドラインの引数を渡す
    c->set_current_argc(argc < NYU_MAXARGS ? argc - 1 : NYU_MAXARGS);
    NyuValue* vals = c->current_argv();
    for (int i = 0, j = c->current_argc(); i < j; i++) {
        vals[i] = NYU_OBJECT_TO_VALUE(NyuStrPool::get(::strdup(argv[i + 1])));
    }

	NyuTree* tree;
	if (yyparse(&tree) != 0) {
		std::cout << "ParseError" << std::endl;
        ::fclose(yyin);
		delete c;
		return 1;
	}

    ::fclose(yyin);

	tree->eval(c);

    delete tree;
	delete c;

	return 0;
}
